/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 abstract_tile.h

	$Header: /resource_editor/abstract_tile.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( ABSTRACT_TILE_H_INCLUDED )
#define ABSTRACT_TILE_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <iosfwd>
#include <vector>

#include "copy_on_write_ptr.h"
#include "player_color.h"
#include "sized_int_types.h"
#include "skill.h"
#include "terrain.h"

// ---------------------------------------------------------------
// tile vertex information
// ---------------------------------------------------------------

int const k_max_tile_vertex_height = 255;
int const k_tile_vertex_light_bits = 15;
int const k_max_tile_vertex_light = ( 1 << k_tile_vertex_light_bits );

// -------------------------------------------------------------------
// transition mask information
// -------------------------------------------------------------------
int const k_transition_set_size_water_to_land = 5;
int const k_transition_set_size_land_to_water = 5;
int const k_transition_set_size_road  = 5;
int const k_transition_set_size_land  = 5;

int const k_transition_set_water_to_land = 0;
int const k_transition_set_land_to_water =	k_transition_set_water_to_land 
											+ k_transition_set_size_water_to_land;
int const k_transition_set_road          =	k_transition_set_land_to_water
											+ k_transition_set_size_land_to_water;
int const k_transition_set_land          =	k_transition_set_road + k_transition_set_size_road;
int const k_transition_set_size          =	k_transition_set_land + k_transition_set_size_land;

// -------------------------------------------------------------------
// forward declarations
// -------------------------------------------------------------------
class t_player;

// -------------------------------------------------------------------
// tile visited types
// -------------------------------------------------------------------
enum t_tile_visibility
{
	k_tile_never_explored,
	k_tile_not_visible,
	k_tile_visible,

	k_tile_visibility_count,
};

// -------------------------------------------------------------------
// class for terrain transition information within a tile
// -------------------------------------------------------------------
class t_terrain_transition
{
public:
	// Member functions
	t_terrain_type	get_terrain() const;
	int				get_terrain_subtype() const;
	unsigned		get_id() const;
	unsigned		get_mask_set() const;
	void			set_terrain( t_terrain_type new_terrain );
	void			set_terrain_subtype( int new_subtype );
	void			set_id( unsigned new_id );
	void			set_mask_set( unsigned new_mask_set );

private:
	// Data members
	unsigned	m_terrain : 5;
	unsigned	m_terrain_subtype : 1;
	unsigned	m_id : 9;         // 0-255 normal transition, 256-259 special corner transition
	unsigned	m_mask_set : 5;   // Actual transition set used, 0-19.
};

inline t_terrain_type t_terrain_transition::get_terrain() const
{
	return t_terrain_type( m_terrain );
}

inline int t_terrain_transition::get_terrain_subtype() const
{
	return m_terrain_subtype;
}

inline unsigned t_terrain_transition::get_id() const
{
	return m_id;
}

inline unsigned t_terrain_transition::get_mask_set() const
{
	return m_mask_set;
}

inline void t_terrain_transition::set_terrain( t_terrain_type new_terrain )
{
	assert( new_terrain >= 0 && new_terrain < k_terrain_count );
	m_terrain = new_terrain;
}

inline void t_terrain_transition::set_terrain_subtype( int new_subtype )
{
	assert( new_subtype >= 0 && new_subtype < k_terrain_subtype_max );
	m_terrain_subtype = new_subtype;
}

inline void t_terrain_transition::set_id( unsigned new_id )
{
	m_id = new_id;
}

inline void t_terrain_transition::set_mask_set( unsigned new_mask_set )
{
	m_mask_set = new_mask_set;
}

// -------------------------------------------------------------------
// class for road transition information within a tile
// -------------------------------------------------------------------
class t_road_transition
{
public:
	// Member functions
	t_road_type	get_road_type() const;
	unsigned	get_id() const;
	unsigned	get_mask_set() const;
	void		set_road_type( t_road_type new_road_type );
	void		set_id( unsigned new_id );
	void		set_mask_set( unsigned new_mask_set );

private:
	// Data members
	unsigned short	m_road_type : 2;
	unsigned short	m_id : 9;         // 0-255 normal transition, 256-259 special corner transition
	unsigned short	m_mask_set : 5;   // Actual transition set used, 0-19.
};

inline t_road_type t_road_transition::get_road_type() const
{
	return t_road_type( m_road_type );
}

inline unsigned t_road_transition::get_id() const
{
	return m_id;
}

inline unsigned t_road_transition::get_mask_set() const
{
	return m_mask_set;
}

inline void t_road_transition::set_road_type( t_road_type new_road_type )
{
	assert( new_road_type >= 0 && new_road_type < k_road_type_count );
	m_road_type = new_road_type;
}

inline void t_road_transition::set_id( unsigned new_id )
{
	m_id = new_id;
}

inline void t_road_transition::set_mask_set( unsigned new_mask_set )
{
	m_mask_set = new_mask_set;
}

// -------------------------------------------------------------------
// abstract map cell, used primarily by graphics engine, derived classes
// created by game and engine
// note that the term "anti-stealth" is used here because it's not exactly
// the same as scouting.  Creatures exert "anti-stealth" even though 
// they have no scouting skill, and cannot give extra information from scouting.
// -------------------------------------------------------------------
class t_abstract_tile
{
public:
	enum { k_current_format_version = 4 };

	// Types
	typedef std::vector< t_road_transition >	t_road_transition_vector;
	typedef std::vector< t_terrain_transition >	t_terrain_transition_vector;

	// Constructor
	t_abstract_tile();

	// Member functions
	void								clear_transitions();
	t_skill_mastery						get_anti_stealth( int team_num ) const;
	t_road_type							get_road_type() const;
	t_road_transition const &			get_road_transition( int transition_num ) const;
	int									get_road_transition_count() const;
	t_road_transition_vector const &	get_road_transition_vector() const;
	t_rock_terrain_type					get_rock_terrain_type() const;
	t_terrain_type						get_terrain() const;
	int									get_terrain_subtype() const;
	t_terrain_transition const &		get_terrain_transition( int transition_num ) const;
	int									get_terrain_transition_count() const;
	t_terrain_transition_vector const &	get_terrain_transition_vector() const;
	t_tile_visibility					get_visibility( int team_num ) const;
	bool								has_rock_terrain_transition() const;
	bool								read( std::streambuf & stream, int version );
	void								set_anti_stealth( int				team_num,
														  t_skill_mastery	new_scouting );
	void								set_road_type( t_road_type new_road_type );
	void								set_rock_terrain_type(
											t_rock_terrain_type	new_rock_terrain_type );
	void								set_rock_terrain_transition(
											bool	new_rock_terrain_transition );
	void								set_terrain( t_terrain_type new_terrain );
	void								set_terrain_subtype( int new_subtype );
	void								set_visibility(
											int					team_num,
											t_tile_visibility	new_visibility,
											t_skill_mastery		new_scouting = k_mastery_none );
	void								swap_transition_vectors(
											t_terrain_transition_vector &	new_terrain_vector,
											t_road_transition_vector &		new_road_vector );
	bool								write( std::streambuf& buffer ) const;

protected:
	// Destructor
	~t_abstract_tile();

private:
	// Types
	struct t_transition_info
	{
		t_terrain_transition_vector	terrain_vector;
		t_road_transition_vector	road_vector;
	};

	typedef t_copy_on_write_ptr< t_transition_info > t_transition_info_ptr;

	// Static member functions
	static t_uint32	compute_default_visibility_data();

	// Data members
	t_uint32				m_terrain : 5;
	t_uint32				m_terrain_subtype : 1;
	t_uint32				m_road_type : 2;
	t_uint32				m_rock_terrain_type : 2;
	t_uint32				m_rock_terrain_transition : 1;
	t_uint32				m_visibility_data : 21; // Encodes the visibility and scouting data in as few bits as possible
	t_transition_info_ptr	m_transition_info_ptr;
};

// -------------------------------------------------------------------
// inlines for t_abstract_tile
// -------------------------------------------------------------------
inline t_abstract_tile::t_abstract_tile()
	:	m_terrain( k_terrain_water ),
		m_terrain_subtype( 0 ),
		m_road_type( k_road_none ),
		m_rock_terrain_type( k_rock_terrain_none ),
		m_visibility_data( compute_default_visibility_data() ),
		m_rock_terrain_transition( false )
{
}

inline t_abstract_tile::~t_abstract_tile()
{
}

inline void t_abstract_tile::clear_transitions()
{
	m_transition_info_ptr = t_transition_info_ptr();
}

inline t_road_type t_abstract_tile::get_road_type() const
{
	return t_road_type( m_road_type );
}

inline t_road_transition const &
t_abstract_tile::get_road_transition( int transition_num ) const
{
	assert(		transition_num >= 0
			&&	transition_num < (int)m_transition_info_ptr->road_vector.size() );
	return m_transition_info_ptr->road_vector[ transition_num ];
}

inline int t_abstract_tile::get_road_transition_count() const
{
	return m_transition_info_ptr->road_vector.size();
}

inline t_abstract_tile::t_road_transition_vector const &
t_abstract_tile::get_road_transition_vector() const
{
	return m_transition_info_ptr->road_vector;
}

inline t_rock_terrain_type t_abstract_tile::get_rock_terrain_type() const
{
	return t_rock_terrain_type( m_rock_terrain_type );
}

inline t_terrain_type t_abstract_tile::get_terrain() const
{
	return t_terrain_type( m_terrain );
}

inline int t_abstract_tile::get_terrain_subtype() const
{
	return m_terrain_subtype;
}

inline t_terrain_transition const &
t_abstract_tile::get_terrain_transition( int transition_num ) const
{
	assert(		transition_num >= 0
			&&	transition_num < (int)m_transition_info_ptr->terrain_vector.size() );
	return m_transition_info_ptr->terrain_vector[ transition_num ];
}

inline int t_abstract_tile::get_terrain_transition_count() const
{
	return m_transition_info_ptr->terrain_vector.size();
}

inline t_abstract_tile::t_terrain_transition_vector const &
t_abstract_tile::get_terrain_transition_vector() const
{
	return m_transition_info_ptr->terrain_vector;
}

inline bool t_abstract_tile::has_rock_terrain_transition() const
{
	return m_rock_terrain_transition != 0;
}

inline void t_abstract_tile::set_road_type( t_road_type new_road_type )
{
	assert( new_road_type >= 0 && new_road_type < k_road_type_count );
	m_road_type = new_road_type;
}

inline void t_abstract_tile::set_rock_terrain_type(
	t_rock_terrain_type	new_rock_terrain_type )
{
	assert(		new_rock_terrain_type >= 0
			&&	new_rock_terrain_type < k_rock_terrain_type_count );
	m_rock_terrain_type = new_rock_terrain_type;
}

inline void t_abstract_tile::set_rock_terrain_transition(
	bool	new_rock_terrain_transition )
{
	m_rock_terrain_transition = new_rock_terrain_transition;
}

inline void t_abstract_tile::set_terrain( t_terrain_type new_terrain )
{
	assert( new_terrain >= 0 && new_terrain < k_terrain_count );
	m_terrain = new_terrain;
}

inline void t_abstract_tile::set_terrain_subtype( int new_subtype )
{
	assert( new_subtype >= 0 && new_subtype < k_terrain_subtype_max );
	m_terrain_subtype = new_subtype;
}

inline void t_abstract_tile::swap_transition_vectors(
	t_terrain_transition_vector &	new_terrain_vector,
	t_road_transition_vector &		new_road_vector )
{
	t_transition_info_ptr new_transition_info_ptr;
	if ( !new_terrain_vector.empty() )
		new_transition_info_ptr->terrain_vector.swap( new_terrain_vector );
	if ( !new_road_vector.empty() )
		new_transition_info_ptr->road_vector.swap( new_road_vector );
	m_transition_info_ptr = new_transition_info_ptr;
}

// -------------------------------------------------------------------
// t_abstract_tile_vertex class
// -------------------------------------------------------------------

class t_abstract_tile_vertex
{
public:
	// Constructor
	t_abstract_tile_vertex();

	// Member functions
	int		get_height() const;
	int		get_light() const;
	void	set_height( int new_height );
	void	set_light( int new_light );

protected:
	// Destructor
	~t_abstract_tile_vertex();

private:
	// Static member functions
	static int compute_default_light();

	// Data members
	unsigned short	m_height;
	unsigned short	m_light;
};

inline t_abstract_tile_vertex::t_abstract_tile_vertex()
	:	m_height( 0 )
{
	static int const k_default_light = compute_default_light();
	m_light = k_default_light;
}

inline t_abstract_tile_vertex::~t_abstract_tile_vertex()
{
}

inline int t_abstract_tile_vertex::get_height() const
{
	return m_height;
}

inline int t_abstract_tile_vertex::get_light() const
{
	return m_light;
}

inline void t_abstract_tile_vertex::set_height( int new_height )
{
	assert( new_height >= 0 && new_height <= k_max_tile_vertex_height );
	m_height = new_height;
}

inline void t_abstract_tile_vertex::set_light( int new_light )
{
//	assert( new_light >= 0 && new_light <= k_max_tile_vertex_light );
	m_light = new_light;
}

// -------------------------------------------------------------------
// Non-member functions
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// -------------------------------------------------------------------
int compute_edge_light( int left_height, int right_height );

#endif // !defined( ABSTRACT_TILE_H_INCLUDED )
